<template>
    <div class="upload">
        <el-upload
            :key="largeFile ? 'large' : 'normal'"
            v-model:file-list="fileList"
            ref="uploadRefs"
            :action="action"
            :multiple="multiple"
            :limit="limit"
            :show-file-list="false"
            :headers="headers"
            :data="data"
            :on-progress="handleProgress"
            :on-success="handleSuccess"
            :on-exceed="handleExceed"
            :on-error="handleError"
            :accept="getAccept"
            :disabled="disabled"
            :auto-upload="largeFile ? false : true"
            :http-request="largeFile? customHttpRequest: undefined"
            :on-change="handleChange"
        >
        <!-- 
        
         -->
            <slot />
        </el-upload>
        <el-dialog
            v-if="showProgress && fileList.length"
            v-model="visible"
            title="上传进度"
            :close-on-click-modal="false"
            width="500px"
            :modal="false"
            @close="handleClose"
        >
            <div class="file-list p-4">
                <template v-for="(item, index) in fileList" :key="index">
                    <div class="mb-5">
                        <div>{{ item.name }}</div>
                        <div class="flex-1">
                            <el-progress :percentage="parseInt(largeFile? item.nums: item.percentage)" />
                        </div>
                    </div>
                </template>
            </div>
        </el-dialog>
    </div>
</template>

<script lang="ts">
import { computed, defineComponent, ref, shallowRef } from 'vue'
import useUserStore from '@/stores/modules/user'
import config from '@/config'
import feedback from '@/utils/feedback'
import type { ElUpload } from 'element-plus'
import { RequestCodeEnum } from '@/enums/requestEnums'
import axios from 'axios'
// 分片大小 (1MB)
const CHUNK_SIZE = 1 * 1024 * 1024
export default defineComponent({
    components: {},
    props: {
        // 上传文件类型
        type: {
            type: String,
            default: 'image'
        },
        // 是否支持多选
        multiple: {
            type: Boolean,
            default: true
        },
        // 多选时最多选择几条
        limit: {
            type: Number,
            default: 10
        },
        // 上传时的额外参数
        data: {
            type: Object,
            default: () => ({})
        },
        // 是否显示上传进度
        showProgress: {
            type: Boolean,
            default: false
        },
        disabled: {
            type: Boolean,
            default: false
        },
        largeFile: {
            type: Boolean,
            default: false
        }
    },
    emits: ['change', 'error', 'success', 'allSuccess'],
    setup(props, { emit }) {
        const userStore = useUserStore()
        const uploadRefs = shallowRef<InstanceType<typeof ElUpload>>()
        const action = computed(() => 
            props.largeFile 
                ? `${config.baseUrl}${config.urlPrefix}/upload/chunkUpload` 
                : `${config.baseUrl}${config.urlPrefix}/upload/${props.type}`
        )
        // console.log(action)
        const headers = computed(() => ({
            token: userStore.token,
            version: config.version
        }))
        const visible = ref(false)
        const fileList = ref<any[]>([])
        const uploadingFiles = ref<Map<string, { file: File; chunks: number; uploadedChunks: number; cancelToken: any }>>(new Map())
        const handleChange = () => {
            console.log('handleChange')
            console.log(props.largeFile)
            if (uploadRefs.value && props.largeFile) {
                uploadRefs.value.submit()
            } else {
                return false
            }
        }
        // 自定义上传方法
        const customHttpRequest = async (options: any) => {
            console.log(options)
            const { file, onProgress, onError, onSuccess } = options
            
            // 生成文件唯一标识
            const fileKey = `${file.name}-${file.size}-${file.lastModified}`
            
            // 如果已经在上传中，则跳过
            if (uploadingFiles.value.has(fileKey)) {
                return
            }
            
            // 计算分片总数
            const chunks = Math.ceil(file.size / CHUNK_SIZE)
            
            // 创建取消令牌
            const CancelToken = axios.CancelToken
            const source = CancelToken.source()
            
            // 添加上传中的文件
            uploadingFiles.value.set(fileKey, {
                file,
                chunks,
                uploadedChunks: 0,
                cancelToken: source
            })
            // console.log(fileList.value)
            
            // 更新文件列表显示
            const fileIndex = fileList.value.findIndex(f => f.uid === file.uid)
            // console.log(fileIndex)
            if (fileIndex === -1) {
                fileList.value.push({
                    ...file,
                    percentage: 0,
                    nums: 0,
                    status: 'uploading'
                })
            } else {
                fileList.value[fileIndex].percentage = 0
                fileList.value[fileIndex].nums = 0
                fileList.value[fileIndex].status = 'uploading'
                // console.log(fileList.value)
            }
            
            visible.value = true
            
            try {
                // 上传所有分片
                for (let chunk = 0; chunk < chunks; chunk++) {
                    const start = chunk * CHUNK_SIZE
                    const end = Math.min(start + CHUNK_SIZE, file.size)
                    const fileChunk = file.slice(start, end)
                    
                    const formData = new FormData()
                    formData.append('file', fileChunk)
                    formData.append('chunk', chunk.toString())
                    formData.append('chunks', chunks.toString())
                    formData.append('file_type', getFileTypeCode(props.type).toString())
                    formData.append('file_name', file.name)
                    
                    // 添加额外参数
                    Object.keys(props.data).forEach(key => {
                        formData.append(key, props.data[key])
                    })
                    
                    await axios.post(action.value, formData, {
                        headers: headers.value,
                        cancelToken: source.token,
                        onUploadProgress: (progressEvent) => {
                            // console.log(progressEvent)

                            // 计算整体进度
                            // console.log(fileList.value)
                            const uploadedChunks = uploadingFiles.value.get(fileKey)?.uploadedChunks || 0
                            // console.log(uploadedChunks)
                            // console.log(chunks)
                            // console.log((uploadedChunks + (progressEvent.loaded / progressEvent.total)) / chunks)
                            const totalProgress = Math.floor(((uploadedChunks + (progressEvent.loaded / progressEvent.total)) / chunks) * 100)
                            // console.log(totalProgress)
                            // console.log(fileList.value[0])
                            // 更新进度
                            const fileIndex = fileList.value.findIndex(f => f.uid === file.uid)
                            // console.log(fileIndex)
                            if (fileIndex !== -1) {
                                // console.log(Math.min(totalProgress, 100))
                                fileList.value[fileIndex].percentage = Math.min(totalProgress, 100)
                                fileList.value[fileIndex].nums = Math.min(totalProgress, 95)
                                fileList.value = [...fileList.value]
                                // console.log(fileList.value)
                            }
                            
                            if (onProgress) {
                                onProgress(progressEvent, file)
                            }
                        }
                    })
                    
                    // 更新已上传分片数
                    if (uploadingFiles.value.has(fileKey)) {
                        const uploadInfo = uploadingFiles.value.get(fileKey)!
                        uploadInfo.uploadedChunks++
                        
                        // 如果所有分片都已上传，从上传列表中移除
                        if (uploadInfo.uploadedChunks >= uploadInfo.chunks) {
                            uploadingFiles.value.delete(fileKey)
                        }
                    }
                }
                
                // 上传成功
                const fileIndex = fileList.value.findIndex(f => f.uid === file.uid)
                if (fileIndex !== -1) {
                    fileList.value[fileIndex].status = 'success'
                }
                // console.log('上传成功')
                // 模拟成功响应（实际应该由服务器返回合并后的文件信息）
                const response = {
                    code: RequestCodeEnum.SUCCESS,
                    data: {
                        url: 'https://example.com/path/to/file', // 实际应该是服务器返回的文件URL
                        name: file.name,
                        size: file.size,
                        type: props.type
                    }
                }
                
                if (onSuccess) {
                    onSuccess(response, file)
                }
                handleSuccess(response, file, fileList.value)
                
            } catch (error: any) {
                // 上传失败
                const fileIndex = fileList.value.findIndex(f => f.uid === file.uid)
                if (fileIndex !== -1) {
                    fileList.value[fileIndex].status = 'fail'
                }
                
                // 从上传列表中移除
                uploadingFiles.value.delete(fileKey)
                
                if (axios.isCancel(error)) {
                    console.log('Upload canceled:', error.message)
                } else {
                    if (onError) {
                        onError(error, file)
                    }
                    handleError(error, file)
                }
            }
        }

        // 获取文件类型代码
        const getFileTypeCode = (type: string): number => {
            switch (type) {
                case 'image': return 10
                case 'video': return 20
                default: return 30
            }
        }

        const handleProgress = (event: any, file: any, fileLists: any[]) => {
            if (!props.largeFile) {
                visible.value = true
            }
        }
        let uploadLen = 0
        const handleSuccess = (response: any, file: any, fileLists: any[]) => {
            uploadLen++
            if (uploadLen == fileList.value.length) {
                uploadLen = 0
                fileList.value = []
                emit('allSuccess')
            }
            emit('change', file)
            if (response.code == RequestCodeEnum.SUCCESS) {
                emit('success', response)
            }
            if (response.code == RequestCodeEnum.FAIL && response.msg) {
                feedback.msgError(response.msg)
            }
        }
        const handleError = (event: any, file: any) => {
            uploadLen++
            if (uploadLen == fileList.value.length) {
                uploadLen = 0
                fileList.value = []
                emit('allSuccess')
            }
            feedback.msgError(`${file.name}文件上传失败`)
            uploadRefs.value?.abort(file)
            visible.value = false
            emit('change', file)
            emit('error', file)
        }
        const handleExceed = () => {
            feedback.msgError(`超出上传上限${props.limit}，请重新上传`)
        }
        const handleClose = () => {
            if (props.largeFile) {
                // 取消所有正在上传的文件
                uploadingFiles.value.forEach(uploadInfo => {
                    uploadInfo.cancelToken.cancel('你已取消所有上传')
                })
                uploadingFiles.value.clear()
            }
            fileList.value = []
            visible.value = false
        }

        const getAccept = computed(() => {
            switch (props.type) {
                case 'image':
                    return '.jpg,.png,.gif,.jpeg,.ico'
                case 'video':
                    return '.wmv,.avi,.mpg,.mpeg,.3gp,.mov,.mp4,.flv,.rmvb,.mkv'
                default:
                    return '*'
            }
        })
        watch(() => props.largeFile, (newVal) => {
            uploadLen = 0
        }, { immediate: true })
        return {
            uploadRefs,
            action,
            headers,
            visible,
            fileList,
            getAccept,
            customHttpRequest,
            handleProgress,
            handleSuccess,
            handleError,
            handleExceed,
            handleClose,
            handleChange
        }
    }
})
</script>

<style lang="scss"></style>
